#!/usr/bin/python
# -*- coding: iso8859 -*-

class ZipHandler ( object ):

	def __init__ ( self ):
		"""
		ZipHandler ()

		Constructor.
		"""

		import zipfile
		from os.path import basename, isdir, isfile, join, dirname, split
		from os import mkdir

		self.attrs = { 
			"CREATE_DIRS": 1,		# Flag T/F. Se 1 (T) crea le directories
			"REMOVE_LEADING_SLASH": 1,	# Flag T/F. Se 1 (T) rimuove eventuali "/" all'inizio del nome del file
			"VERBOSE":	0,		# Flag T/F. Se 1 (T) stampa messaggi sullo schermo
			"EXTRACT_ZERO_LEN": 0,		# Flag T/F. Se 1 estrae anche i files di lunghezza 0
			"OVERWRITE":	0,		# Flag T/F. Se 1 (T) sovrascrive i files
			"ZIP_DIR":	"."		# Dir dove verranno aperti i files (deve esistere!)
		}

		self.file = None

		# Binding from original modules
		self.__basename = basename
		self.__dirname  = dirname
		self.__split    = split
		self.__zip = zipfile
		self.__mkdir = mkdir
		self.__isfile = isfile
		self.__isdir = isdir
		self.__join = join


	def close ( self ):
		"""Closes an open zip file."""

		if ( self.file ): self.file.close ()

		self.file = None


	def set_attr ( self, name, val ):
		"""Use this method to set an attribute of the class. Avaible attributes are:
			
			CREATE_DIRS		- Flag T/F. If 1 (T), directories are created during extraction. (Def: 1)
			REMOVE_LEADING_SLASH	- Flag T/F. If 1 (T), leading "/" slashes are removed from the file name. (Def: 1)
			VERBOSE			- Flag T/F. If 1 (T), some messages will appear on the console during work. (Def: 0)
			EXTRACT_ZERO_LEN	- Flag T/F. If 1 (T), zero length file are extracted too. (Def: 0)
			OVERWRITE		- Flag T/F. If 1 (T), files with the same name are overwritten. (Def: 0)

			ZIP_DIR			- Base dir name where files will be extracted. It *MUST* exists. (Def: ".")
		"""
		if ( self.attrs.has_key ( name ) ):
			self.attrs [ name ] = val;
		else:
			print "ERROR: unknown attribute: " + name


	def read ( self, file_name ):
		"""Opens an existsing zip file for reading (extract).
		   You must call this function before any other operation.
		"""

		if ( self.file ): self.file.close()
		self.file = self.__zip.ZipFile ( file_name, "r" )


	def list ( self ):
		"""List all the file names in the current zip file opened for reading.
		   Returns a list of file names.
		"""

		if ( self.file == None ): return []
		return [ f for f in self.file.namelist () ]


	def info ( self ):
		"""List all the file names and attributes in the current zip file opened for reading.
		   Returns a list of lists with this fields [ name, date_time, file_size ].
		"""

		if ( self.file == None ): return []
		return [ [ f.filename, f.date_time, f.file_size ] for f in self.file.infolist () ]


	def extract ( self, list = None ):
		"""Extract one or more files from the currently open zip.
		   You can provide as 'list' a list of valid file names within the current zip file,
		   or None, which means you want to open the whole archive.

		   The extraction behaves as you would expect by the settings you gave using set_attr() calls.

		   Returns a list of fully qualified file names of files extracted.
		"""
		if ( self.file == None ): return 1

		if ( list == None ): list = self.list ()
			
		create_dirs = self.attrs [ "CREATE_DIRS" ]
		dest_dir    = self.attrs [ "ZIP_DIR" ]
		verbose	    = self.attrs [ "VERBOSE" ]
		zero_len    = self.attrs [ "EXTRACT_ZERO_LEN" ]
		overwrite   = self.attrs [ "OVERWRITE" ]

		res = []

		for f in list:
			if ( verbose ): print "Extract: " + f
			data = self.file.read ( f )

			if ( create_dirs ): self.__make_dirs ( f, dest_dir, res );

			if ( ( zero_len == 0 ) and ( len ( data ) == 0 ) ): 
				if ( verbose ): print "Skipping: " + f + " - Zero Length File";
				continue

			if ( create_dirs == 0 ): f = self.__basename ( f )

			fname = self.__join ( dest_dir, f )

			if ( ( self.__isfile ( fname ) ==  1 ) and ( overwrite == 0 ) ):
				if ( verbose ): print "Skipping: " + f + " - File Already Exists";
				continue

			fout = open ( fname, "wb" )
			fout.write ( data )
			fout.close ()

			res.append ( fname )

		return ( res )

	def __make_dirs ( self, file_name, dest_dir, res ):
		if ( file_name [-1] == '/' ): 
			self.__mk_dir ( self.__join ( dest_dir, file_name ), res )
		else:
			n = self.__dirname ( file_name )
			dirs = n.split ( "/" )

			for f in dirs:
				self.__mk_dir ( self.__join ( dest_dir, f ), res )


	def __mk_dir ( self, name, res ):
		if ( len ( name ) == 0 ): return

		#print "MKDIR: " + name
		if ( self.__isdir ( name ) ): return

		#print "Making: " + name
		self.__mkdir ( name )
		res.append ( name )


if __name__ == '__main__':
	p = ZipHandler ()

	p.set_attr ( "CREATE_DIRS", 1 )
	p.set_attr ( "ZIP_DIR", "out" )
	p.set_attr ( "VERBOSE", 1 )
	p.set_attr ( "EXTRACT_ZERO_LEN", 1 )
	p.set_attr ( "OVERWRITE", 1 )

	p.read ( "/home/fabio/ofiles.zip" )
	print p.extract ()
	#print p.list ()
	p.close ()
